﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace Zombie_Demo.User_Interface
{
     [Serializable()]
     [XmlRoot("Element")]
     [XmlInclude(typeof(Button)), XmlInclude(typeof(BackgroundRectangle)), XmlInclude(typeof(Frame)), XmlInclude(typeof(Text))]
     public abstract class Element
     {
          private Vector2 position;
          private Element parent;
          private bool toRemove = false;
          private List<Element> elements;
          private AnchorPosition anchor;
          protected Rectangle bounds;
          protected bool mouseOver = false;
          protected bool isMouseDragging = false;
          protected string name;
          protected bool hidden = false;
          public enum AnchorPosition { UPPERLEFT, UPPERRIGHT, LOWERLEFT, LOWERRIGHT, CENTER, LOWERCENTER, UPPERCENTER };

          /// <summary>
          /// If set, the element will be removed next update.
          /// </summary>
          [XmlIgnore]
          public bool ToRemove { get { return this.toRemove; } set { this.toRemove = value; } }
          /// <summary>
          /// The position of the element inside its parent.
          /// </summary>
          public Vector2 Position { get { return this.position; } set { this.position = value; this.bounds.X = (int)value.X; this.bounds.Y = (int)value.Y; } }
          /// <summary>
          /// A list of all the elements contained in this one.
          /// </summary>
          public List<Element> Elements { get { return this.elements; } set { this.elements = value; } }
          /// <summary>
          /// The parent element of this one.
          /// </summary>
          [XmlIgnore]
          public Element Parent { get { return this.parent; } set { this.parent = value; } }
          /// <summary>
          /// The position from which to draw the referenced position.
          /// </summary>
          public AnchorPosition Anchor { get { return this.anchor; } set { this.anchor = value; } }

         /// <summary>
         /// A simple bounding rectangle as all elements are currently square. Really easy for click detection
         /// </summary>
          public Rectangle Bounds { get { return this.bounds; } set { this.bounds = value; } }

         /// <summary>
         /// Var to access if the mouse if over any given element at any given time
         /// </summary>
          public bool MouseOver { get { return this.mouseOver; } }

         /// <summary>
         /// A human readble name of the element
         /// </summary>
          public string Name { get { return this.name; } set { this.name = value; } }

         /// <summary>
         /// True to not render the element
         /// </summary>
          public bool Hidden { get { return this.hidden; } set { this.hidden = value; } }

          /// <summary>
          /// Create a new blank Element.
          /// </summary>
          public Element() { 
               elements = new List<Element>();
               this.Position = Vector2.Zero;
               this.anchor = AnchorPosition.UPPERLEFT;
               this.parent = null;
               this.name = "";
          }

          /// <summary>
          /// Create a new Element.
          /// </summary>
          /// <param name="parent">The parent element. Automatically adds the created element to the parent's list.</param>
          public Element(Element parent) { 
               elements = new List<Element>(); 
               this.parent = parent; 
               this.parent.elements.Add(this);
               this.Position = Vector2.Zero;
               this.anchor = AnchorPosition.UPPERLEFT;
          }

         /// <summary>
         /// Create a new element
         /// </summary>
          /// <param name="parent">The parent element. Automatically adds the created element to the parent's list.</param>
         /// <param name="position">The V2D of the anchor in parent coordinates</param>
          public Element(Element parent, Vector2 position)
          {
               elements = new List<Element>();
               this.parent = parent;
               this.parent.elements.Add(this);
               this.Position = position;
               this.anchor = AnchorPosition.UPPERLEFT;
          }

         /// <summary>
         /// Create a new element
         /// </summary>
          /// <param name="parent">The parent element. Automatically adds the created element to the parent's list.</param>
         /// <param name="bounds">Outside bounds of the element</param>
          public Element(Element parent, Rectangle bounds)
          {
              elements = new List<Element>();
              this.parent = parent;
              this.parent.elements.Add(this);
              this.Position = new Vector2(bounds.X, bounds.Y);
              this.bounds = bounds;
              this.anchor = AnchorPosition.UPPERLEFT;
          }

          /// <summary>
          /// Load the element, should only be called once.
          /// </summary>
          public virtual void Load() 
          {
              //Need to find a way to load item names durring load. and do it top down, rather than stackwise - bottom up
              //Load items during load... really clever Rat :/
              if (this.position.X <= 1 && this.position.X > 0)
              {
                  this.position.X = this.parent.bounds.Width * this.position.X;
              }

              if (this.position.Y <= 1 && this.position.Y > 0)
              {
                  this.position.Y = this.parent.bounds.Height * this.position.Y;
              }

               if (!(this.parent == null)) 
               {
                    if (this.anchor == AnchorPosition.UPPERLEFT)
                    {
                         this.Position += this.parent.Position;
                    }
                    else if (this.anchor == AnchorPosition.CENTER)
                    {
                         Vector2 pos = Vector2.Zero;
                         pos.X = this.parent.Position.X + (((parent.bounds.Width / 2) - (this.bounds.Width / 2)) + this.Position.X);
                         pos.Y = this.parent.Position.Y + (((parent.bounds.Height / 2) - (this.bounds.Height / 2)) + this.Position.Y);
                         this.Position = pos;
                        //Maths to find the center of a parent and offset with Position
                    }
                    else if (this.anchor == AnchorPosition.LOWERCENTER)
                    {
                        Vector2 pos = Vector2.Zero;
                        pos.X = this.parent.Position.X + (((parent.bounds.Width / 2) - (this.bounds.Width / 2)) + this.Position.X);
                        pos.Y = this.Parent.bounds.Bottom + this.position.Y;
                        this.Position = pos;
                        //Maths to find the bottom center of a parent and offset with Position
                    }
               } 
               foreach (Element e in elements) 
                    e.Load(); 
          }

          /// <summary>
          /// Will remove the element and references in the parent.
          /// </summary>
          public virtual void UnLoad() { this.toRemove = true; this.parent.elements.Remove(this); }

          /// <summary>
          /// Update the element's children.
          /// </summary>
          /// <param name="gameTime">The GameTime variable.</param>
          public virtual void Update(GameTime gameTime) {
              if (!this.hidden)
              {
                  if (Utilities.MouseWrapper.MouseBox.Intersects(this.bounds))
                  {
                      this.mouseOver = true;
                  }
                  else
                  {
                      this.mouseOver = false;
                  }
                  foreach (Element e in elements) e.Update(gameTime);
              }
          }

          /// <summary>
          /// Draw the element's children.
          /// </summary>
          /// <param name="gameTime">The GameTime variable.</param>
          /// <param name="spriteBatch">The SpriteBatch being used to draw.</param>
          public virtual void Draw(GameTime gameTime, SpriteBatch spriteBatch) { if(!hidden) foreach (Element e in elements) e.Draw(gameTime, spriteBatch); }

          /// <summary>
          /// Set the parent of this element to another.
          /// </summary>
          /// <param name="e">The element to set the parent to.</param>
          public void SetParent(Element e) { this.parent = e; if (!(this.parent.elements.Contains(this))) { this.parent.elements.Add(this); } }

          /// <summary>
          /// Add a given element to this elements list. Will also set this element as the parent.
          /// </summary>
          /// <param name="e">The element to add.</param>
          public void AddElement(Element e) { if (!(this.elements.Contains(e))) { this.elements.Add(e); } e.SetParent(this); }

         /// <summary>
         /// Sets all children to their parent (sometimes in list but not set). CALL ONLY ONCE!
         /// </summary>
          public virtual void SetElementsToParent()
          {
               foreach (Element e in elements)
               {
                    e.SetParent(this);
                    e.SetElementsToParent();
               }
          }
     }
}